// StdLib.cs
//
// Copyright (c) 2009 Jérémie Laval and Sylvain Marchand
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

using System;
using System.Text;
using System.Reflection;
using System.Collections.Generic;

using Microsoft.Linq.Expressions;

namespace Bechamel
{
	public static class StdLib
	{
		internal static MethodInfo GetByName(string name)
		{
			return typeof(StdLib).GetMethod(name);
		}
		
		public static void Exit()
		{
			Environment.Exit(0);
		}

		public static void Display(object obj)
		{
			Pair pair = obj as Pair;
			if (pair != null) {
				Console.Write(GetPairAsString(pair));
				return;
			}

			Console.Write(obj.ToString());
		}

		public static object Call(object type, object method)
		{
			string typeStr = type as string;
			string methodStr = method as string;
			
			Type t = Type.GetType(typeStr);
			
			MethodInfo meth = t.GetMethod(methodStr);
			if (meth == null) {
				PropertyInfo prop = t.GetProperty(methodStr);
				if (prop != null)
					meth = prop.GetGetMethod();
			}
			
			return (meth != null) ? meth.Invoke(null, null) :
				null;
		}

		static string GetPairAsString(Pair pair)
		{
			StringBuilder sb = new StringBuilder("(");
			string separator = pair.IsAList ? " " : " . ";

			Pair current = pair;
			while (current != null && current != Pair.EmptyList) {
				Pair temp = current.Car as Pair;
				if (temp != null) {
					Console.WriteLine("Foo");
					// Take care of nested list
					sb.Append(GetPairAsString(temp));
				} else {
					sb.Append(current.Car.ToString());
				}

				temp = current.Cdr as Pair;
				if (temp == null) {
					sb.Append(separator);
					sb.Append(current.Cdr.ToString());
					break;
				} else {
					if (temp != Pair.EmptyList)
						sb.Append(separator);
					current = temp;
				}
			}

			sb.Append(")");

			return sb.ToString();
		}

		public static Pair Cons(object val1, object val2)
		{
			return new Pair(val1, val2);
		}

		public static object Car(object obj)
		{
			Pair pair = obj as Pair;
			if (pair == null)
				return false;
			
			return pair.Car;
		}

		public static object Cdr(object obj)
		{
			Pair pair = obj as Pair;
			if (pair == null)
				return false;
			
			return pair.Cdr;
		}

		public static void SetCar(object pair, object val)
		{
			Pair p = pair as Pair;
			if (p == null)
				return;
			p.Car = val;
		}

		public static void SetCdr(object pair, object val)
		{
			Pair p = pair as Pair;
			if (p == null)
				return;
			p.Car = val;
		}

		public static string StringAppend(object string1, object string2)
		{
			string str1 = string1.ToString();
			string str2 = string2.ToString();
			if (str1 == null || str2 == null)
				throw new TypeMismatchException("string-append", str1 == null ? "1" : "2");

			return string.Concat(str1, str2);			
		}

		public static Pair Reverse(object list)
		{
			Pair result = Pair.EmptyList;
			Pair node = list as Pair;
			if (node == null)
				throw new TypeMismatchException("reverse", "1");

			while (node != Pair.EmptyList) {
				Pair temp = new Pair(node.Car, result, true);
				result = temp;

				node = node.Cdr as Pair;
				if (node == null)
					throw new TypeMismatchException("reverse", "1");
			}

			return result;
		}

		public static bool IsPair(object obj)
		{
			return obj is Pair;
		}

		public static bool IsNumber(object obj)
		{
			return obj is Int32;
		}

		public static bool IsString(object obj)
		{
			return obj is String;
		}

		public static bool IsNull(object obj)
		{
			Pair p = obj as Pair;

			return p != null && p == Pair.EmptyList;
		}

		public static bool IsList(object obj)
		{
			Pair p = obj as Pair;

			return p != null && p.IsAList;
		}
	}
}
